package com.study.spring.factory;

import com.study.spring.RootPackage;
import com.study.spring.annotation.Autowired;
import com.study.spring.annotation.Service;
import com.study.spring.annotation.Transactional;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.reflections.Reflections;


/**
 * @author lupengwei
 * <p>
 * BeanFactory 注解方式实现
 */
public class BeanFactory {

    private static final Map<String, Object> OBJECT_MAP = new HashMap<>();

    /**
     * 初始化容器，注意这里并没有考虑循环依赖问题
     */
    public void init() {
        OBJECT_MAP.clear();

        ProxyFactory proxyFactory = new ProxyFactory();
        String rootPackageName = RootPackage.class.getPackage().getName();
        Reflections reflections = new Reflections(rootPackageName);

        // 1、创建 Bean
        Set<Class<?>> classSet = reflections.getTypesAnnotatedWith(Service.class);
        for (Class<?> aClass : classSet) {
            // 如果没有自定义 beanName，使用用类型的名称作为 beanName
            Service serviceAnnotation = aClass.getAnnotation(Service.class);
            String beanName = serviceAnnotation.value();
            beanName = "".equals(beanName) ? getSimpleName(aClass) : beanName;

            try {
                Object instance = aClass.getDeclaredConstructor().newInstance();
                OBJECT_MAP.put(beanName, instance);
            } catch (InstantiationException |
                IllegalAccessException |
                InvocationTargetException |
                NoSuchMethodException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }
        }

        // 2、注入依赖
        for (Class<?> aClass : classSet) {
            // 获取这个类的全部字段，只有添加 @Autowired 注解的字段需要自动注入
            Field[] declaredFields = aClass.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                // 获取自己
                String targetBeanName = getSimpleName(aClass);
                Object targetBean = OBJECT_MAP.get(targetBeanName);
                if (targetBean == null) {
                    continue;
                }

                // 开始注入依赖
                Autowired autowiredAnnotation = declaredField.getAnnotation(Autowired.class);
                if (autowiredAnnotation != null) {
                    // 如果没有自定义 beanName，就使用属性名称作为 beanName
                    String beanName = autowiredAnnotation.value();
                    if ("".equals(beanName)) {
                        beanName = declaredField.getName();
                    }

                    // 使用属性名称作为 beanName 获取 Bean，如果不存在就使用类型名称作为 beanName
                    Object injectBean = OBJECT_MAP.get(beanName);
                    if (injectBean == null) {
                        beanName = getSimpleName(declaredField.getType());
                        injectBean = OBJECT_MAP.get(beanName);
                    }

                    if (injectBean != null) {
                        declaredField.setAccessible(true);
                        try {
                            // 属性注入
                            declaredField.set(targetBean, injectBean);
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                            throw new RuntimeException(e);
                        }
                    }
                }
            }
        }
    }

    public static Object getBean(String beanName) {
        Object instance = OBJECT_MAP.get(beanName);
        if (instance == null) {
            return null;
        }

        ProxyFactory proxyFactory = (ProxyFactory) OBJECT_MAP.get("proxyFactory");
        Class<?> aClass = instance.getClass();
        // 根据是否有 @Transactional 来决定是否创建代理类
        if (isMethodWithTransactionalAnnotation(aClass)) {
            // 实现了接口就使用动态代理，否则使用 CGLIB 创建代理
            if (aClass.getInterfaces().length != 0) {
                return proxyFactory.getJdkProxy(instance);
            } else {
                return proxyFactory.getCglibProxy(instance);
            }
        } else {
            return instance;
        }
    }


    private String getSimpleName(Class<?> aClass) {
        return toLowerCaseFirstChar(aClass.getSimpleName());
    }


    private static String toLowerCaseFirstChar(String s) {
        if (Character.isLowerCase(s.charAt(0))) {
            return s;
        } else {
            return Character.toLowerCase(s.charAt(0)) + s.substring(1);
        }
    }


    private static boolean isMethodWithTransactionalAnnotation(Class<?> aClass) {
        Method[] declaredMethods = aClass.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            Transactional annotation = declaredMethod.getAnnotation(Transactional.class);
            if (annotation != null) {
                return true;
            }
        }
        return false;
    }
}
